/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pin.db.connectionPool;

import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;

/**
 *
 * @author shoaly
 */
public class PinConnection implements Connection, PooledConnection {

    List<StatementEventListener> statementEventListeners = new ArrayList<StatementEventListener>();
    private static Hashtable<String, PinStatement> statementTable = new Hashtable<String, PinStatement>();
    private long startTime;
    private boolean running;
    private PinDataSource ds;
    Connection conn;

    public PinConnection(Connection conn) {
        this.conn = conn;
    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    PinConnection(Connection conn, PinDataSource ds) {
        this.conn = conn;
        this.ds = ds;

    }

    public void addConnectionEventListener(ConnectionEventListener listener) {
        ds.addConnectionEventListener((PinConnectionEventListener) listener);
    }

    public void addStatementEventListener(StatementEventListener listener) {
        statementEventListeners.add(listener);
    }

    public Connection getConnection() throws SQLException {
        return conn;
    }

    public void removeConnectionEventListener(ConnectionEventListener listener) {
        ds.removeConnectionEventListener((PinConnectionEventListener) listener);
    }

    public void removeStatementEventListener(StatementEventListener listener) {
        statementEventListeners.remove(listener);
    }

    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        conn.setTypeMap(map);
    }

    public void setTransactionIsolation(int level) throws SQLException {
        conn.setTransactionIsolation(level);
    }

    public Savepoint setSavepoint(String name) throws SQLException {
        return conn.setSavepoint(name);
    }

    public Savepoint setSavepoint() throws SQLException {
        return conn.setSavepoint();
    }

    public void setReadOnly(boolean readOnly) throws SQLException {
        conn.setReadOnly(readOnly);
    }

    public void setHoldability(int holdability) throws SQLException {
        conn.setHoldability(holdability);
    }

    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        conn.setClientInfo(properties);
    }

    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        conn.setClientInfo(name, value);
    }

    public void setCatalog(String catalog) throws SQLException {
        conn.setCatalog(catalog);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {
        conn.setAutoCommit(autoCommit);
    }

    public void rollback(Savepoint savepoint) throws SQLException {
        conn.rollback(savepoint);
    }

    public void rollback() throws SQLException {
        conn.rollback();
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        conn.releaseSavepoint(savepoint);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void closeStatement(PinStatement pstmt) throws SQLException {
        //pstmt.getStatement().close();
        PinStatementEvent event = new PinStatementEvent(pstmt.getConnection(), pstmt, PinStatementEvent.EVENT_CLOSE);
        trigger(event);
    }

    public PinStatement prepareStatement(String sql) throws SQLException {

        if (statementTable.contains(sql)) {
            PinStatement pstmt = statementTable.get(sql);
            pstmt.addUsedTime();
            PinStatementEvent event = new PinStatementEvent(pstmt.getConnection(), pstmt, PinStatementEvent.EVENT_OPEN);
            return pstmt;
        } else {
            PinStatement pstmt = new PinStatement(conn.prepareStatement(sql), this);
            statementTable.put(sql, pstmt);
            pstmt.addUsedTime();
            pstmt.setStartTime(System.currentTimeMillis());
            PinStatementEvent event = new PinStatementEvent(pstmt.getConnection(), pstmt, PinStatementEvent.EVENT_CREATE);
            trigger(event);
            return pstmt;
        }
    }

    /**
    public synchronized void sweepStatement() throws SQLException {
    Collection<PinStatement> pstmts = statementTable.values();
    int shouldSweep = statementTable.size() - 10;//maxPreparedStatements;
    TreeSet<Long> s = new TreeSet<Long>();
    for (PinStatement pstmt : pstmts) {
    long startTime = pstmt.getStartTime();
    int usedTimes = pstmt.getUsedTimes();
    long time = System.currentTimeMillis() - startTime;
    long freq = usedTimes / time;
    s.add(freq);
    }
    Iterator<Long> it = s.iterator();
    long threshold = Long.MIN_VALUE;
    for (int i = 0; i < shouldSweep && it.hasNext(); i++) {
    threshold = it.next();
    }
    for (PinStatement pstmt : pstmts) {
    long startTime = pstmt.getStartTime();
    int usedTimes = pstmt.getUsedTimes();
    long time = System.currentTimeMillis() - startTime;
    long freq = usedTimes / time;
    if (freq < threshold) {
    sweepStatementList.add(pstmt);
    }
    }
    for(PinStatement pstmt:sweepStatementList){
    pstmt.getStatement().close();
    }
    sweepStatementList.clear();
    for(String key:statementTable.keySet()){
    if(statementTable.get(key).isClosed()){
    statementTable.remove(key);
    }
    }
    }
     */
    private void trigger(EventObject event) {
        if (event instanceof PinStatementEvent) {
            PinStatementEvent pinEvent = (PinStatementEvent) event;
            for (StatementEventListener listener : statementEventListeners) {
                if (listener instanceof PinStatementEventListener) {
                    PinStatementEventListener pinListener = (PinStatementEventListener) listener;
                    final int eventType = pinEvent.getEventType();
                    switch (eventType) {
                        case PinStatementEvent.EVENT_CLOSE:
                            pinListener.statementClosed(pinEvent);
                            break;
                        case PinStatementEvent.EVENT_CREATE:
                            pinListener.statementCreated(pinEvent);
                            break;
                        case PinStatementEvent.EVENT_DESTROY:
                            pinListener.statementDestoried(pinEvent);
                            break;
                        case PinStatementEvent.EVENT_OPEN:
                            pinListener.statementOpened(pinEvent);
                            break;

                    }
                }
            }
        }
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
    }

    public CallableStatement prepareCall(String sql) throws SQLException {
        return conn.prepareCall(sql);
    }

    public String nativeSQL(String sql) throws SQLException {
        return conn.nativeSQL(sql);
    }

    public boolean isValid(int timeout) throws SQLException {
        return conn.isValid(timeout);
    }

    public boolean isReadOnly() throws SQLException {
        return conn.isReadOnly();
    }

    public boolean isClosed() throws SQLException {
        return conn.isClosed();
    }

    public SQLWarning getWarnings() throws SQLException {
        return conn.getWarnings();
    }

    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return conn.getTypeMap();
    }

    public int getTransactionIsolation() throws SQLException {
        return conn.getTransactionIsolation();
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        return conn.getMetaData();
    }

    public int getHoldability() throws SQLException {
        return conn.getHoldability();
    }

    public Properties getClientInfo() throws SQLException {
        return conn.getClientInfo();
    }

    public String getClientInfo(String name) throws SQLException {
        return conn.getClientInfo(name);
    }

    public String getCatalog() throws SQLException {
        return conn.getCatalog();
    }

    public boolean getAutoCommit() throws SQLException {
        return conn.getAutoCommit();
    }

    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return conn.createStruct(typeName, attributes);
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return conn.createStatement(resultSetType, resultSetConcurrency);
    }

    public Statement createStatement() throws SQLException {
        return conn.createStatement();
    }

    public SQLXML createSQLXML() throws SQLException {
        return conn.createSQLXML();
    }

    public NClob createNClob() throws SQLException {
        return conn.createNClob();
    }

    public Clob createClob() throws SQLException {
        return conn.createClob();
    }

    public Blob createBlob() throws SQLException {
        return conn.createBlob();
    }

    public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return conn.createArrayOf(typeName, elements);
    }

    public void commit() throws SQLException {
        conn.commit();
    }

    public void close() throws SQLException {
        
        for (PinStatement pstmt : statementTable.values()) {
            closeStatement(pstmt);
        }
        statementTable.clear();
        //this.conn=null;
        ds.closeConnection(this);
    }
    

    void destory() throws SQLException {
        conn.close();
    }

    public void clearWarnings() throws SQLException {
        conn.clearWarnings();
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {        
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
//市场估算
//泛世纪
